using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using ApplicationLayer;
using Blokus.Controls;
using PersistenceLayer;

#if (DEBUG)
using NUnit.Framework;
using Blokus.Controls;
using PersistenceLayer;
using ApplicationLayer;
#endif

namespace Blokus.Scenes
{
#if (DEBUG)
    [TestFixture]
#endif

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class SceneDesigner : Scene
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont SaveFont;
        SpriteFont GameText;

        Texture2D rectangleTexture;
        Texture2D px;
        Texture2D star;
        Texture2D cell;
        Texture2D cursor;
        int cellSize;
        Rectangle starLocation;
        Rectangle blockLocation;
        Rectangle rect32x32;
        Rectangle canvas;

        Point mouseLocation;
        Point SaveButtonLocation;

        List<Point> CellsToDraw;
        List<Point> StartLocations;
        ApplicationLayer.GamePiece gamePiece;
        ApplicationLayer.GameBoard gameBoard;
        enum GridType
        {
            Board,
            Piece,
        }

        bool pieceMode;

        GridType Type;
        Point gridStartPosition;
        int offsetx, offsety;
        int lineWidth;
        bool moveBoard;

        int rows, columns;
        Rectangle rowRectangle;
        Rectangle columnRectangle;
        TextBox txtRow;
        TextBox txtColumn;
        TextBox txtName;
        TextBox btnSave;
        ButtonState laststate;
        KeyboardState lastKeyboardState;


        private KeyboardState keyPrevState;
        private GamePadState gpPrevState;
        private Blokus game;

        public SceneDesigner(Blokus game, int rows, int columns)
        {
            this.game = game;
            this.rows = rows;
            this.columns = columns;
            if (rows == 5 && columns == 5)
                this.Type = GridType.Piece;
            else
                this.Type = GridType.Board;

            txtRow = new TextBox(rows.ToString(), false, new Rectangle(200, 100, 50, 20));
            txtColumn = new TextBox(columns.ToString(), false, new Rectangle(200, 150, 50, 20));

            txtName = new TextBox("Untitled", false, new Rectangle(100, 350, 150, 20));

            moveBoard = false;
            gamePiece = new ApplicationLayer.GamePiece();
            gameBoard = new ApplicationLayer.GameBoard();
            mouseLocation = new Point();
            SaveButtonLocation = new Point(100, 400);
            cellSize = 400 / Math.Max(rows, columns);
            if (cellSize < 10)
                cellSize = 10;
            offsetx = 350;
            offsety = 50;
            if (Type == GridType.Piece)
            {
                offsetx = 550;
                offsety = 200;
            }
            int largerNum = Math.Max(rows, columns);
            if (largerNum <= 10)
            {
                lineWidth = 5;
            }
            else if (largerNum <= 15)
            {
                lineWidth = 3;
            }
            else if (largerNum <= 20)
            {
                lineWidth = 2;
            }
            else
            {
                lineWidth = 1;
            }
            laststate = ButtonState.Released;
            lastKeyboardState = Keyboard.GetState();
            CellsToDraw = new List<Point>();
            StartLocations = new List<Point>();
            gridStartPosition = new Point(offsetx, offsety);
            starLocation = new Rectangle(150, 250, 40, 40);
            blockLocation = new Rectangle(200, 250, 40, 40);
            rect32x32 = new Rectangle(0, 0, 32, 32);
            pieceMode = true;
        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        public override void LoadContent(ContentManager content, GraphicsDeviceManager graphics)
        {
            this.graphics = graphics;
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

            rectangleTexture = content.Load<Texture2D>("Textures/rectangle");
            px = content.Load<Texture2D>("pxTan");
            cursor = content.Load<Texture2D>("Textures/cursor");
            cell = content.Load<Texture2D>("Textures/gamesquare");
            star = content.Load<Texture2D>("Textures/star");
            SaveFont = content.Load<SpriteFont>("SaveFont");
            GameText = content.Load<SpriteFont>("GameText");
            #region TextBoxes
            txtRow.backgroundTexture = rectangleTexture;
            txtRow.spriteBatch = spriteBatch;
            txtRow.GameFont = GameText;
            txtRow.Header = "Rows: ";
            txtRow.intOnly = true;
            txtColumn.spriteBatch = spriteBatch;
            txtColumn.backgroundTexture = rectangleTexture;
            txtColumn.GameFont = GameText;
            txtColumn.Header = "Columns: ";
            txtColumn.intOnly = true;
            txtName.spriteBatch = spriteBatch;
            txtName.backgroundTexture = rectangleTexture;
            txtName.GameFont = GameText;
            txtName.Header = "Name: ";
            btnSave = new TextBox("Save", false, new Rectangle(SaveButtonLocation.X, SaveButtonLocation.Y, (int)SaveFont.MeasureString("Save").X + 5, (int)SaveFont.MeasureString("Save").Y + 5));
            btnSave.spriteBatch = spriteBatch;
            btnSave.GameFont = SaveFont;
            btnSave.colorOnMouseOver = true;
            btnSave.backgroundTexture = rectangleTexture;
            #endregion
            rowRectangle = new Rectangle(80, 100, 230, (int)GameText.MeasureString("Row: 200").Y);
            columnRectangle = new Rectangle(80, 110 + (int)GameText.MeasureString("Row: 200").Y, 230, (int)SaveFont.MeasureString("Column: 200").Y);
            canvas = new Rectangle(280, 0, graphics.GraphicsDevice.Viewport.Width - 300, graphics.GraphicsDevice.Viewport.Height);
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public int milliDemoCounter = 0;
        public override Blokus.GameState Update(GameTime gameTime)
        {
            game.IsMouseVisible = true;
            //if (isInitialized)
            //{
            MouseState ms = Mouse.GetState();
            KeyboardState keyboardState = Keyboard.GetState();
            if (keyboardState.IsKeyDown(Keys.Escape))
            {
                return Blokus.GameState.DESIGNER_MENU;
            }
            moveBoard = keyboardState.IsKeyDown(Keys.Space);

            mouseLocation = new Point(ms.X, ms.Y);

            #region Move GameBoard
            if (Type == GridType.Board)
            {
                #region Mouse Regions
                if (FreqUsed.isInRectangle(ms, new Rectangle(canvas.X, canvas.Y, 50, canvas.Height)))
                {
                    MoveGridLeft();
                }
                if (FreqUsed.isInRectangle(ms, new Rectangle(canvas.Right - 50, canvas.Y, 50, canvas.Height)))
                {
                    MoveGridRight();
                }
                if (FreqUsed.isInRectangle(ms, new Rectangle(canvas.X, canvas.Y, canvas.Width, 50)))
                {
                    MoveGridUp();
                }
                if (FreqUsed.isInRectangle(ms, new Rectangle(canvas.X, canvas.Bottom - 50, canvas.Width, 50)))
                {
                    MoveGridDown();
                }
                #endregion

                #region Keys
                if (lastKeyboardState != keyboardState && keyboardState.IsKeyDown(Keys.Left))
                {
                    MoveGridRight();
                }
                else if (lastKeyboardState != keyboardState && keyboardState.IsKeyDown(Keys.Right))
                {
                    MoveGridLeft();
                }
                else if (lastKeyboardState != keyboardState && keyboardState.IsKeyDown(Keys.Up))
                {
                    MoveGridDown();
                }
                else if (lastKeyboardState != keyboardState && keyboardState.IsKeyDown(Keys.Down))
                {
                    MoveGridUp();
                }
                #endregion
            }
            #endregion

            if (laststate != ButtonState.Pressed)
            {
                if (ms.LeftButton == ButtonState.Pressed && ms.X > offsetx && ms.Y > offsety && ms.X < offsetx + (cellSize * columns) && ms.Y < offsety + (cellSize * rows)) //start checking coords
                {
                    //Get cell mouse clicked
                    int currentRow = (ms.Y - offsety) / cellSize;
                    int currentColumn = (ms.X - offsetx) / cellSize;

                    bool canAdd = true;
                    if (Type == GridType.Piece)
                    {
                        canAdd = false;
                        //Make sure they are attaching to an adjacent point
                        Point addPoint = new Point(cellSize * currentColumn + offsetx, cellSize * currentRow + offsety);
                        Point leftpoint = new Point(cellSize * (currentColumn - 1) + offsetx, cellSize * currentRow + offsety);
                        Point rightpoint = new Point(cellSize * (currentColumn + 1) + offsetx, cellSize * currentRow + offsety);
                        Point uppoint = new Point(cellSize * currentColumn + offsetx, cellSize * (currentRow - 1) + offsety);
                        Point downpoint = new Point(cellSize * currentColumn + offsetx, cellSize * (currentRow + 1) + offsety);
                        if ((CellsToDraw.Contains(leftpoint) || CellsToDraw.Contains(rightpoint) || CellsToDraw.Contains(uppoint) || CellsToDraw.Contains(downpoint)))
                        {
                            canAdd = true;
                        }
                    }
                    if (CellsToDraw.Count == 0 || canAdd)
                    {
                        #region Add the selected square
                        Point location = new Point(cellSize * currentColumn + offsetx, cellSize * currentRow + offsety);
                        if (pieceMode && !StartLocations.Contains(location))
                        {
                            CellsToDraw.Add(location);
                            if (Type == GridType.Piece)
                            {
                                gamePiece.points.Add(new Point(currentColumn, currentRow));
                            }
                            else if (Type == GridType.Board)
                            {
                                gameBoard.unplayable.Add(new Point(currentColumn, currentRow));
                            }
                        }
                        else if (!CellsToDraw.Contains(location))
                        {
                            StartLocations.Add(location);
                            if (Type == GridType.Piece)
                            {
                                gamePiece.points.Add(new Point(currentColumn, currentRow));
                            }
                            else if (Type == GridType.Board)
                            {
                                gameBoard.startingCells.Add(new Point(currentColumn, currentRow));
                            }
                        }
                        #endregion
                    }
                }
                else if (ms.RightButton == ButtonState.Pressed && ms.X > offsetx && ms.Y > offsety) //start checking coords
                {
                    int currentRow = (ms.Y - offsety) / cellSize;
                    int currentColumn = (ms.X - offsetx) / cellSize;
                    CellsToDraw.Remove(new Point(cellSize * currentColumn + offsetx, cellSize * currentRow + offsety));
                    StartLocations.Remove(new Point(cellSize * currentColumn + offsetx, cellSize * currentRow + offsety));
                }
                else if (ms.LeftButton == ButtonState.Pressed && Type == GridType.Board && ApplicationLayer.FreqUsed.isInRectangle(ms, blockLocation))
                {
                    pieceMode = true;
                }

                else if (ms.LeftButton == ButtonState.Pressed && Type == GridType.Board && ApplicationLayer.FreqUsed.isInRectangle(ms, starLocation))
                {
                    pieceMode = false;
                }
                else if (btnSave.Update(gameTime, ms))
                {
                    btnSave.hasFocus = false;
                    if (Type == GridType.Piece)
                    {
                        PDesigner.pieces.Add(gamePiece);
                        this.gamePiece = new ApplicationLayer.GamePiece();
                        CellsToDraw.Clear();
                    }
                    else
                    {
                        gameBoard.name = txtName.Text;
                        txtName.Text = "Untitled";
                        PDesigner.boards.Add(gameBoard);
                        this.gameBoard = new ApplicationLayer.GameBoard();
                        StartLocations.Clear();
                        CellsToDraw.Clear();
                    }
                    
                    return Blokus.GameState.DESIGNER_MENU;
                }

            }
            txtName.Update(gameTime, ms);
            int oldRows = rows;
            int oldColumns = columns;
            if (txtRow.Update(gameTime, ms))
            {
                txtColumn.hasFocus = false;
            }
            if (txtColumn.Update(gameTime, ms))
            {
                txtRow.hasFocus = false;
            }
            if (txtColumn.Text.Length > 0)
            {
                columns = Convert.ToInt32(txtColumn.Text);
            }
            if (txtRow.Text.Length > 0)
            {
                rows = Convert.ToInt32(txtRow.Text);
            }
            if (oldRows != rows || oldColumns != columns)
            {
                StartLocations.Clear();
                CellsToDraw.Clear();
                offsetx = 350;
                offsety = 50;
                gridStartPosition = new Point(offsetx, offsety);
                gameBoard = new ApplicationLayer.GameBoard();
                gamePiece = new ApplicationLayer.GamePiece();
            }
            laststate = ms.LeftButton;
            lastKeyboardState = keyboardState;

            // }
            // else //Run Initialization Update Code
            // {
            // }
            if (Type == GridType.Piece)
                return Blokus.GameState.DESIGNER_PIECE;
            else //Type == GridType.Board
                return Blokus.GameState.DESIGNER_BOARD;
        }

        private void MoveGridUp()
        {
            gridStartPosition.Y += 5;
            offsety += 5;
            List<Point> tempPoints = new List<Point>();
            foreach (Point p in CellsToDraw)
            {
                tempPoints.Add(new Point(p.X, p.Y + 5));
            }
            CellsToDraw = tempPoints;
            tempPoints = new List<Point>();
            foreach (Point p in StartLocations)
            {
                tempPoints.Add(new Point(p.X, p.Y + 5));
            }
            StartLocations = tempPoints;
        }

        private void MoveGridDown()
        {
            gridStartPosition.Y -= 5;
            offsety -= 5;
            List<Point> tempPoints = new List<Point>();
            foreach (Point p in CellsToDraw)
            {
                tempPoints.Add(new Point(p.X, p.Y - 5));
            }
            CellsToDraw = tempPoints;
            tempPoints = new List<Point>();
            foreach (Point p in StartLocations)
            {
                tempPoints.Add(new Point(p.X, p.Y - 5));
            }
            StartLocations = tempPoints;
        }

        private void MoveGridLeft()
        {
            gridStartPosition.X += 5;
            offsetx += 5;
            List<Point> tempPoints = new List<Point>();
            foreach (Point p in CellsToDraw)
            {
                tempPoints.Add(new Point(p.X + 5, p.Y));
            }
            CellsToDraw = tempPoints;
            tempPoints = new List<Point>();
            foreach (Point p in StartLocations)
            {
                tempPoints.Add(new Point(p.X + 5, p.Y));
            }
            StartLocations = tempPoints;
        }

        private void MoveGridRight()
        {
            gridStartPosition.X -= 5;
            offsetx -= 5;
            List<Point> tempPoints = new List<Point>();
            foreach (Point p in CellsToDraw)
            {
                tempPoints.Add(new Point(p.X - 5, p.Y));
            }
            CellsToDraw = tempPoints;
            tempPoints = new List<Point>();
            foreach (Point p in StartLocations)
            {
                tempPoints.Add(new Point(p.X - 5, p.Y));
            }
            StartLocations = tempPoints;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(colorDark);
            //if (isInitialized)
            //{
            int scale = Math.Max(columns, rows);
            spriteBatch.Begin();

            spriteBatch.Draw(rectangleTexture, canvas, new Color(51, 78, 125)); //Dark Background


            for (int row = 0; row < rows * cellSize; row += cellSize) //Draw the grid
            {
                for (int column = 0; column < columns * cellSize; column += cellSize)
                {
                    if (gridStartPosition.X + row - 50 > canvas.Left && gridStartPosition.Y + column - 50 > canvas.Top)
                    {
                        spriteBatch.Draw(cell, new Rectangle(gridStartPosition.X + row, gridStartPosition.Y + column, cellSize, cellSize), rect32x32, Color.White);
                    }
                }
            }
            foreach (Point p in CellsToDraw) //Fill in the piece locations
            {
                Color c = Color.Red;
                if (Type == GridType.Board)
                {
                    c = Color.Black;
                }
                if (p.X - 50 > canvas.Left && p.Y - 50 > canvas.Top)
                {
                    spriteBatch.Draw(cell, new Rectangle(p.X, p.Y, cellSize, cellSize), rect32x32, c);
                }

            }
            foreach (Point p in StartLocations)
            {
                if (p.X - 50 > canvas.Left && p.Y - 50 > canvas.Top)
                {
                    spriteBatch.Draw(star, new Rectangle(p.X, p.Y, cellSize, cellSize), rect32x32, Color.White);
                }
            }
            spriteBatch.Draw(rectangleTexture, new Rectangle(0, 0, 280, graphics.GraphicsDevice.Viewport.Height), new Color(192, 204, 224));
            if (Type == GridType.Board)
            {
                if (pieceMode)
                {
                    blockLocation.Inflate(4, 4);
                    spriteBatch.Draw(cell, blockLocation, Color.LightYellow); //Highlight our mode
                    blockLocation.Inflate(-4, -4);
                }
                else
                {
                    starLocation.Inflate(4, 4);
                    spriteBatch.Draw(cell, starLocation, Color.LightYellow); //Highlight our mode
                    starLocation.Inflate(-4, -4);
                }

                spriteBatch.Draw(star, starLocation, rect32x32, Color.White); //Draw the star
                spriteBatch.Draw(cell, blockLocation, rect32x32, Color.DarkGray); //Draw the block
            }

            btnSave.Draw();
            if (Type == GridType.Board)
            {
                txtRow.Draw();
                txtColumn.Draw();
                txtName.Draw();
                //spriteBatch.DrawString(GameText, string.Format("Rows: {0}", rows), new Vector2(rowRectangle.X, rowRectangle.Y), Color.Black);
                //spriteBatch.DrawString(GameText, string.Format("Columns: {0}", columns), new Vector2(columnRectangle.X, columnRectangle.Y), Color.Black);
            }
            //Save Button Border
            //spriteBatch.Draw(px, new Rectangle(SaveButtonLocation.X, SaveButtonLocation.Y, 100, lineWidth), Color.Black); //Top
            //spriteBatch.Draw(px, new Rectangle(SaveButtonLocation.X, SaveButtonLocation.Y + 50, 100 + lineWidth, lineWidth), Color.Black); //Bottom
            //spriteBatch.Draw(px, new Rectangle(SaveButtonLocation.X, SaveButtonLocation.Y, lineWidth, 50), Color.Black); //Left
            //spriteBatch.Draw(px, new Rectangle(SaveButtonLocation.X + 100, SaveButtonLocation.Y, lineWidth, 50), Color.Black); //Right
            ////Save Button Text
            //spriteBatch.DrawString(SaveFont, "Save", new Vector2(SaveButtonLocation.X + 7, SaveButtonLocation.Y), Color.Black);
            //spriteBatch.Draw(cursor, new Rectangle(mouseLocation.X, mouseLocation.Y, cellSize - cellSize / 4, cellSize - cellSize / 4), new Rectangle(0, 0, 201, 337), Color.White);//Draw Mouse pixel
            spriteBatch.End();
            //}
        }

        public override void ResetPrevKeys()
        {
            keyPrevState = Keyboard.GetState();
            gpPrevState = GamePad.GetState(PlayerIndex.One);
            SceneHighscores.newHighscore = -1;
            milliDemoCounter = 0;
        }


#if (DEBUG)
        [Test]
        public void Test01_CheckConstructs()
        {

            SceneDesigner sd = new SceneDesigner(game, 5/*rows*/, 5 /*columns*/);
            Assert.AreEqual(5, sd.rows);
            Assert.AreEqual(5, sd.columns);
            sd.rows = 10;
            sd.columns = 20;
            Assert.AreEqual(10, sd.rows);
            Assert.AreEqual(20, sd.columns);

        }
#endif

    }
}
